#pragma once

#include"Sock.hpp"
#include<iostream>
#include<functional>
#include<string>



namespace select_ns
{
    class SelectServer
    {
        static const int default_port = 8080;
        static const int fd_num = sizeof(fd_set)*8;
        static const int defaultfd = -1;

        using func_t = std::function<std::string(const std::string&)>;
    public:
        SelectServer(func_t func,int port = default_port)
            : port_(port),listensock_(-1),fdarray_(nullptr),func_(func)
        {}

        void initServer()
        {
            listensock_ = Sock::Socket();//创建套接字
            Sock::Bind(listensock_,port_);//绑定套接字
            Sock::Listen(listensock_);    // 把套接字设置为监听状态！
            fdarray_ = new int[fd_num];
            for(int i = 0;i<fd_num;i++) fdarray_[i] = defaultfd;
            // 初始化该数组！
            fdarray_[0] =listensock_;
        }
        void Print()
        {
            for(int i = 0;i<fd_num;i++)
            {
                if (fdarray_[i] != defaultfd)
                    std::cout << "fd list: " << fdarray_[i] << " ";
            }
            std::cout << std::endl;
        }


        void Accepter(int listensock)
        {
            // select告诉我们，listensock读事件就绪了！
            std::string ClientIp;
            uint16_t ClientPort = 0;
            int sock = Sock::Accept(listensock, &ClientIp, &ClientPort); // 获取新连接
            // 走到这里accept函数不会阻塞！
            if (sock == ACCEPT_ERR)
                return;

            // 将新的sock托管给select！
            // 而托管给select的本质就是，将sock添加到fdarray_这数组中！
            // 当下一次循环的时候，就会被托管进select里面！
            int i = 0;
            for (i = 0; i < fd_num; i++)
            {
                if (fdarray_[i] != defaultfd)
                    continue;
                break;
            }

            if (i == fd_num) // 这说明所有的数组元素都是合法的！
            {
                LogMessage(WARNING, "server is full! please wait!");
                close(sock); // 关闭这个新的套接字
            }
            else
            {
                fdarray_[i] = sock;
            }
            Print();
        }


        void Recver(int sock,int pos)//pos参数是为了方便我们需要的时候删除掉这个sock
        {
            //1.读取
            char buffer[1024];
            ssize_t s = recv(sock,buffer,sizeof(buffer) -1,0);//这里再进行读取的时候就不会被阻塞了！
            //这样子的读写时有问题的！因为不能肯定能不能把缓冲区的数据都读取上来！
            //也不能保证读一半的时候该怎么办！序列化和反序列化该怎么处理！但是为了方便我演示我们就暂时怎么写！
            if(s > 0)
            {
                buffer[s] = 0;
                LogMessage(NORMAL,"Client# %s",buffer);
            }
            else if(s == 0)//对方关闭文件描述符！
            {
                close(sock);//我们也要关闭自己的这边的文件描述符！
                //然后让select不要再关心这个文件描述符了！
                fdarray_[pos] = defaultfd;
                LogMessage(NORMAL,"client quit! me too!");
                return;
            }
            else //读取失败
            {
                close(sock);//我们也要关闭自己的这边的文件描述符！
                //然后让select不要再关心这个文件描述符了！
                fdarray_[pos] = defaultfd;
                LogMessage(ERROR,"read error! errno: %s,error string: %s",errno,strerror(errno));
                return;
            }

            //2.处理request
            std::string response = func_(buffer);

            //返回response
            //write——我们这里打算把结果给用户返回！
            //但是我们如何保证写入的时候写事件就就绪了呢？——那么我们就要给select多加一个新的writefds
            //同时也要有一个新的数组来维护这些fd！但是因为这样子会让代码更加的复杂！我们这里就不进行实现！
            //所以我们就直接写回去了！暂时不考虑写回去的暂停问题！

            write(sock, response.c_str(), response.size());
        }
        void HandlerEvent(fd_set& rfds)
        {
            //我们无法知道那个文件描述符对应的事件是什么！
            //所以我们只能对所有的文件描述符进行遍历！
            for(int i = 0;i<fd_num;i++)
            {
                //过滤非法fd
                if(fdarray_[i]== defaultfd) continue;//非法fd不进行处理！

                //从这里开始就是正常的fd！但是正常的fd不一定就绪了！

                // if (FD_ISSET(listensock_, &rfds))
                if (FD_ISSET(fdarray_[i], &rfds) && fdarray_[i] == listensock_)//listensock就是对应第0个下标！
                {
                    std::cout << "this is accept"<< i <<std::endl;
                    Accepter(listensock_);
                }
                else if(fdarray_[i] != defaultfd && FD_ISSET(fdarray_[i],&rfds))//走到这里就说明把listensock上的事件都处理完毕了！
                {
                    //其他事件就是常规的IO事件！
                    std::cout << "this is Recver"<< i <<std::endl;
                    Recver(fdarray_[i],i);
                }
            }
        }
        void start()
        {
            // fd_set readfds;
            // FD_ZERO(&readfds);//初始化
            // FD_SET(listensock_,&readfds);//将listensock置入readset中
            // // struct timeval timeout = {3,0};
            for(;;)
            {
                //阻塞式写法！
                // std::string ClientIp;
                // uint16_t ClientPort = 0;
                // int sock = Sock::Accept(listensock_,&ClientIp,&ClientPort);//获取新连接

                // if(sock < 0)continue;

                fd_set readfds;
                FD_ZERO(&readfds);             // 初始化

                int maxfd = fdarray_[0];
                for (int i = 0; i < fd_num; i++)
                {
                    if(fdarray_[i] == defaultfd)
                        continue;
                    if (fdarray_[i] > maxfd)//找到最大的fd
                        maxfd = fdarray_[i];

                    FD_SET(fdarray_[i], &readfds); // 将全部合法fd都全部添加都添加到集合里面！
                }
                // std::cout << "maxfd: " << maxfd <<std::endl;

                struct timeval timeout = {1, 0};
                // int n = select(maxfd+1,&readfds,nullptr,nullptr,&timeout);
                int n = select(maxfd+1,&readfds,nullptr,nullptr,nullptr);//设置成阻塞方便演示

                switch(n)
                {
                    case 0://如果返回0说明超时了！
                        LogMessage(NORMAL,"timeout....");
                        break;
                    case -1:
                        LogMessage(WARNING,"select error,code: %d,err string: %s",errno,strerror(errno));
                        break;
                    default:
                        // LogMessage(NORMAL,"get a new link ....");
                        LogMessage(NORMAL,"have a event ready");
                        HandlerEvent(readfds);
                        //说明事件就绪了！但是目前只有一个监听时间！
                        break;
                }


            }
            
        }
        ~SelectServer()
        {
            if(listensock_< 0) close(listensock_);
            if (fdarray_ != nullptr) delete[] fdarray_;
        }

    private:
        int port_;
        int listensock_;
        int *fdarray_;
        func_t func_;//用来传入对请求的处理函数！

    };
}

